package util;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class POMPlace {
	private List<String> dependencies;

	public POMPlace() {
		dependencies = new ArrayList<String>();
	}

	public void addDependency(String dependency) {
		this.dependencies.add(dependency);
	}

	public void addDependencyToPOM(String basePath)
			throws ParserConfigurationException, IOException, SAXException,
			TransformerException {
		InputStream inputXMLFile = getClass().getClassLoader()
				.getResourceAsStream("NecessaryDependencies.xml");
		File outputXMLFile = new File(basePath + "/pom.xml");
		if (!outputXMLFile.exists())
			return;

		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		try {
			DocumentBuilder docBuilder = factory.newDocumentBuilder();
			Document inputDoc = docBuilder.parse(inputXMLFile);
			Document outputDoc = docBuilder.parse(outputXMLFile);

			inputDoc.getDocumentElement().normalize();
			outputDoc.getDocumentElement().normalize();
			Node dependencyTags = outputDoc
					.getElementsByTagName("dependencies").item(0);
			if (dependencyTags == null)
				System.out.println("Can't find dependencies");
			else {
				for (String dependency : dependencies) {
					if (outputDoc.getElementsByTagName(dependency).getLength() == 0) {
						Node dependencyTag = inputDoc.getElementsByTagName(
								dependency).item(0);

						Element e = (Element) dependencyTag;
						if (!findDependency(dependencyTags, e))
							processWithXML(e, outputDoc, dependencyTags);
					}
				}

				Transformer transformer = TransformerFactory.newInstance()
						.newTransformer();
				transformer.setOutputProperty(OutputKeys.INDENT, "yes");
				Result output = new StreamResult(outputXMLFile);
				Source input = new DOMSource(outputDoc);

				transformer.transform(input, output);
			}
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
			throw e;
		} catch (IOException e) {
			e.printStackTrace();
			throw e;
		} catch (SAXException e) {
			e.printStackTrace();
			throw e;
		} catch (TransformerConfigurationException e) {
			e.printStackTrace();
			throw e;
		} catch (TransformerException e) {
			e.printStackTrace();
			throw e;
		}
	}

	private static void processWithXML(Element e, Document outputDoc,
			Node dependencyTags) {
		Element dependencyTagOutputElement = outputDoc
				.createElement("dependency");
		createTag(e, outputDoc, dependencyTagOutputElement, "groupId");
		createTag(e, outputDoc, dependencyTagOutputElement, "artifactId");
		createTag(e, outputDoc, dependencyTagOutputElement, "version");

		dependencyTags.appendChild(dependencyTagOutputElement);
	}

	private static void createTag(Element e, Document outputDoc,
			Element parent, String tagName) {
		Element tag = outputDoc.createElement(tagName);
		tag.setTextContent(e.getElementsByTagName(tagName).item(0)
				.getTextContent());
		parent.appendChild(tag);
	}

	public boolean findDependency(Node rootNode, Element requiredNode) {
		boolean isExist = false;
		NodeList childNodes = rootNode.getChildNodes();
		for (int i = 0; i < childNodes.getLength(); ++i) {
			Node childNode = childNodes.item(i);
			if (childNode.getNodeType() == Node.ELEMENT_NODE) {
				Element e = (Element) childNode;
				isExist = matchBetwwenTwoDependency(e, requiredNode);
			}

			if (isExist)
				break;
		}

		return isExist;
	}

	public boolean matchBetwwenTwoDependency(Element dependencyNode,
			Element requiredNode) {
		boolean isExist = true;
		NodeList dependencyChildNodes = dependencyNode.getChildNodes();
		NodeList requiredChildNodes = requiredNode.getChildNodes();
		int indexOfDependencyChildNode = 0, indexOfRequiredChildNode = 0;
		boolean isFlagDependencyChildNode = false, isFlagRequiredChildNode = false;
		int countDependencyChildNode = 0, countRequiredChildNode = 0;

		while (indexOfDependencyChildNode < dependencyChildNodes.getLength()
				&& indexOfRequiredChildNode < requiredChildNodes.getLength()) {
			Node dependencyChildNode = dependencyChildNodes
					.item(indexOfDependencyChildNode);

			if (!isFlagDependencyChildNode) {
				if (dependencyChildNode.getNodeType() == Node.ELEMENT_NODE) {
					++countDependencyChildNode;
					isFlagDependencyChildNode = true;
				}

				++indexOfDependencyChildNode;
			}

			Node requiredChildNode = requiredChildNodes
					.item(indexOfRequiredChildNode);
			if (!isFlagRequiredChildNode) {
				if (requiredChildNode.getNodeType() == Node.ELEMENT_NODE) {
					++countRequiredChildNode;
					isFlagRequiredChildNode = true;
				}
				++indexOfRequiredChildNode;
			}

			if (isFlagDependencyChildNode && isFlagRequiredChildNode) {
				isFlagDependencyChildNode = false;
				isFlagRequiredChildNode = false;

				Element dependencyChildElement = (Element) dependencyChildNode;
				Element requiredChildElement = (Element) requiredChildNode;
				if (dependencyChildElement.getTagName().equals(
						requiredChildElement.getTagName())) {
					if (!dependencyChildElement.getTextContent().equals(
							requiredChildElement.getTextContent())) {
						isExist = false;
						break;
					}
				} else {
					isExist = false;
					break;
				}
			}
		}

		if (countRequiredChildNode != countDependencyChildNode)
			return false;

		return isExist;
	}
}